<?php
/*
 *      project.class.php
 *
 *      Copyright 2010 Patrice Ferlet <metal3d@gmail.com>
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */
// $Id: project.class.php 27 2010-12-17 11:29:12Z Metal3d $
// $HeadURL: http://valaya.googlecode.com/svn/trunk/utils/project.class.php $
/**
 *  Project class is not instanciated but used to run the current url
 *  call. This is the main class that do everything !
 * */
class Project {

	static $args;
	static $baseurl;
	static $content;
	static $plugins;
	static $moduleconf;
	static $currentModule;
	static $context = array();

	static $displayed = false;

	private function __construct ()	{
		//instance is not allowed
	}

	/**
	 * Launch process, set variables, parse some files... include called module etc...
	 *
	 * */
	public function process ()	{

		self::$baseurl =
            'http://'.$_SERVER["SERVER_NAME"].'/'.dirname ($_SERVER['SCRIPT_NAME']);

		$path =
		isset ($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] :
            '/'._config ('index', 'project');
		$args = explode ('/', preg_replace (';^/;', '', $path));
		$page = array_shift ($args);
		self::$args = $args;

		self::pushContext($page);
		self::preparePlugins ();
		self::beforeSession ();
		Session::start();


		$page = _config ('modulepath').$page."/module.php";
		if (file_exists ($page)) {
			ob_start ();
			self::beforeProcess ();
			include $page;
			self::$content = ob_get_clean ();
			self::beforeDisplay ();
			if (strlen (self::$content) > 0) {
				echo self::$content;

				self::$displayed = true;
			}
			self::afterDisplay ();
			return;
		}
		//else header("HTTP/1.1 404 Not Found");
	}

	/**
	 * Parse module configuration file
	 *
	 * @param string $module
	 * @return object $conf
	 *
	 * */
	private function processModuleConf ($module){
		//Process requested page
		$page = &$module;
		$conf = _config ('modulepath').$page.'/conf.ini';

		$moduleconf = false;

		if (file_exists ($conf)) {
			$config = parse_ini_file ($conf, true);
			$moduleconf = new stdClass;
			foreach ($config as $key =>$val) {
				$moduleconf->$key = new stdClass;
				foreach ($val as $k =>$v) {
					$moduleconf->$key->$k = $v;
				}
			}
		}
		return $moduleconf;
	}
	/**
	 * Return module configuration
	 *
	 * @param string $name
	 * @return object $config
	 * */

	public function getModuleConfig ($name = false) {
		if (!$name)
		return self::$moduleconf;
		return self::processModuleConf ($name);
	}

	/**
	 * Get processed contents
	 *
	 * @return mixed &$content
	 * */
	public function & getContent ()	{
		return self::$content;
	}

	/**
	 * Return base url of your project
	 *
	 * @return string $baseurl
	 * */
	public function getBaseURL ()	{
		return self::$baseurl;
	}

	/**
	 * Process url and return string
	 *
	 * @param string $request
	 * @return string $url_corrected
	 * */
	public function getURL ($request)	{
		$request = preg_replace (';^/;', '', $request);
		return self::$baseurl.'/'.$request;
	}

	/**
	 * Prepare plugins that will be called
	 *
	 * */
	private function preparePlugins ()	{
		if (!is_array (self::$plugins))
		self::$plugins = array ();

		$conf = _config ('plugins', 'project');
		foreach ($conf as $plugin) {
			$parts = explode ('/', $plugin);
			$file =
			_config ('modulepath',
                        'project').$parts[0].'/plugins/'.$parts[1].'.plugin.php';
			if (file_exists ($file)) {
				require_once ($file);
				$class = ucfirst ($parts[1]).'Plugin';
				$obj = new $class;
				if (!isset(self::$plugins[$parts[0]])) self::$plugins[$parts[0]] = array();
				self::$plugins[$parts[0]][]= $obj;
			}
		}
	}
	
	/**
	 * 
	 * Push new context to set module and configuration for inter module calls
	 * @param string $module
	 */
	public function pushContext($module){
		array_push(self::$context, array(self::$currentModule, self::$moduleconf));
		self::$currentModule = $module;
		self::$moduleconf = self::processModuleConf ($module);
	}

	/**
	 * 
	 * Pop context to go back to last context (if context was pushed before)
	 */
	public  function popContext () {
		if(count(self::$context)<1) return;
		$context = array_pop(self::$context);
		self::$currentModule = $context[0];
		self::$moduleconf = $context[1];
	}
	
	/**
	 * Call plugins beforeDisplay
	 * */
	private function beforeDisplay ()	{
		foreach (self::$plugins as $mod=>$plugins) {
			self::pushContext($mod);
			foreach($plugins as $plug){
				Log::write('plugin beforeDisplay', "processing ".get_class($plug));
				$plug->beforeDisplay ();
			}
			self::popContext();
		}
	}

	/**
	 * Call plugins afterDisplay
	 * */
	private function afterDisplay ()	{
		foreach (self::$plugins as $mod=>$plugins) {
			self::pushContext($mod);
			foreach($plugins as $plug){
				Log::write('plugin afterDisplay', "processing ".get_class($plug));
				$plug->afterDisplay ();
			}
			self::popContext();
		}
	}

	/**
	 * Call plugins beforeSession
	 * */
	private function beforeSession ()	{
		foreach (self::$plugins as $mod=>$plugins) {
			self::pushContext($mod);
			foreach($plugins as $plug) {
				Log::write('plugin beforeSession', "processing ".get_class($plug));
				$plug->beforeSession ();
			}
			self::popContext();
		}
	}

	/**
	 * Call plugins beforeProcess
	 * */
	private function beforeProcess ()	{
		foreach (self::$plugins as $mod=>$plugins) {
			self::pushContext($mod);
			foreach($plugins as $plug){
				Log::write('plugin beforeProcess', "processing ".get_class($plug));
				$plug->beforeProcess ();
			}
			self::popContext();
		}
	}

	/**
	 * Return true is echo of content is done
	 *
	 * @return boolean $displayed
	 * */
	public function isDisplayed ()	{
		return self::$displayed;
	}


	/**
	 * Get a class from module directories, check if an overriden class is present and returns this instead
	 *
	 * @param string $name
	 * @return object $new_class
	 * */
	public function getClass ($name = false) {
		$mod = self::$currentModule;
		$parts = explode ('/', $name);
		$class = $parts[0];
		if (count ($parts) > 1) {
			$mod = $parts[0];
			$class = $parts[1];
		}
		$toReturn = null;
		$path = _config ('modulepath').''.$mod.'/'.$class;
		if (file_exists ($path.'.class.php') ) {
			$class = $name;
			require_once $path.'.class.php';
			if (file_exists	($path.'.override.php')) {
				require_once $path.'.override.php';
				$class.= 'Override';
			}
			$class = ucfirst ($class);
			return new $class;
		}
	}

	/**
	 * Override require_once functionnality, called in module, it repairs paths
	 *
	 * @param string $path
	 * */
	public function requireOnce ($path)	{
		try {
			throw new PathException ();
		}
		catch (PathException $e) {
			$trace = $e->getTrace ();
			require_once (dirname ($trace[0]['file']).'/'.$path);
		}
		return $this;
	}
}

class PathException extends Exception{}
